/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package controllers;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.*;
import net.sf.jasperreports.engine.*;
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;
import net.sf.jasperreports.view.JasperViewer;
import vo.GraphsStatsSubReportVO;
import vo.StatsVO;
import vo.TransactionToReportVO;
import ws.login.NormalizedEstablishment;

/**
 *
 * @author Jhovany Amaya
 */
public class CreateReport {

    public CreateReport() {
    }
    
    public static void main(String[] args) {
        File file = new File("D:\\reportePrueba");
        NormalizedEstablishment user = new NormalizedEstablishment();
        user.setUser("Fuck");
        user.setBrandName("Brand fuck");
        user.setNameEstablishment("Establishment fuck");
        Calendar calendar = Calendar.getInstance();
        calendar.set(2012, 0, 15);
        ArrayList<TransactionToReportVO> list = new ArrayList<>();
//        System.out.println("Año:"+calendar.get(Calendar.YEAR));
//        System.out.println("Mes:"+calendar.get(Calendar.MONTH));
//        System.out.println("Día:"+calendar.get(Calendar.DATE));
        for (int i = 0; i < 1000; i++) {
            list.add(new TransactionToReportVO("2012-01-15", "algo", "666"+i*100, "ksajdk", "1656", i%2==0?"1":"2", "dsaldksal"));
        }
        new CreateReport().createStats(file, list, user, calendar.getTime(), true, true, true, true, 'm');
    }

    public boolean createReport(File fileToSave, List<TransactionToReportVO> transactions, NormalizedEstablishment activeUser, String reportType) {
        boolean ret = true;
        HashMap parameters = new HashMap();
        String path = null;
        if (reportType.equals("Todas las transacciones"))
            path = "src/resources/AllTransactionsReport.jrxml";
        else
            path = "src/resources/GenericReport.jrxml";
            
        String savePath = fileToSave.getPath()+".pdf";
        File fpre = new File(path);
        FileInputStream isPre = null;
        JasperReport jasperReportPre = null;
        JasperPrint jasperPrintPre = null;
        
        JRBeanCollectionDataSource cds = new JRBeanCollectionDataSource(transactions);
        try {
            isPre = new FileInputStream(fpre);
        } catch (FileNotFoundException ex) {
            ex.printStackTrace();
        }
        parameters.put("store", String.valueOf(activeUser.getBrandName()));
        parameters.put("branch", activeUser.getNameEstablishment());
        parameters.put("user", activeUser.getUser());
        
        try {
            jasperReportPre = JasperCompileManager.compileReport(isPre);
        } catch (JRException ex) {
            ex.printStackTrace();
            ret = false;
        }
        try {
            jasperPrintPre = JasperFillManager.fillReport(jasperReportPre, parameters, cds);
        } catch (JRException ex) {
            ex.printStackTrace();
            ret = false;
        }
        try {
            JasperExportManager.exportReportToPdfFile(jasperPrintPre, savePath);
            //JasperViewer.viewReport(jasperPrintPre);
        } catch (Exception ex) {
            ex.printStackTrace();
            ret = false;
        }
        
        return ret;
    }
    
    /**
     * 
     * @param fileToSave
     * @param transactions
     * @param activeUser
     * @param transactionsNumber
     * @param amounts
     * @param validTransactions
     * @param invalidTransactions
     * @param groupBy Determina la agrupación de los datos en las series
     * @return 
     */
    public boolean createStats(File fileToSave, List<TransactionToReportVO> transactions, NormalizedEstablishment activeUser, Date date,
            boolean transactionsNumber, boolean amounts, boolean validTransactions, boolean invalidTransactions, char groupBy) {
        boolean ret = true;
        HashMap parameters = new HashMap();
        String path = null;
        path = "src/resources/StatsReport.jrxml";
            
        String savePath = fileToSave.getPath()+".pdf";
        File fpre = new File(path);
        FileInputStream isPre = null;
        JasperReport jasperReportPre = null;
        JasperPrint jasperPrintPre = null;
        
        JRBeanCollectionDataSource cds = buildGraphsStatsVO(transactions, transactionsNumber, amounts, validTransactions, 
                invalidTransactions, groupBy, date);
        try {
            isPre = new FileInputStream(fpre);
        } catch (FileNotFoundException ex) {
            ex.printStackTrace();
        }
        parameters.put("store", String.valueOf(activeUser.getBrandName()));
        parameters.put("branch", activeUser.getNameEstablishment());
        parameters.put("user", activeUser.getUser());
        try {
            jasperReportPre = JasperCompileManager.compileReport(isPre);
        } catch (JRException ex) {
            ex.printStackTrace();
            ret = false;
        }
        try {
            jasperPrintPre = JasperFillManager.fillReport(jasperReportPre, parameters, cds);
        } catch (JRException ex) {
            ex.printStackTrace();
            ret = false;
        }
        try {
            JasperExportManager.exportReportToPdfFile(jasperPrintPre, savePath);
//            JasperViewer.viewReport(jasperPrintPre);
        } catch (Exception ex) {
            ex.printStackTrace();
            ret = false;
        }
        
        return ret;
    }
    
    public static final char GROUP_BY_YEAR='y';
    public static final char GROUP_BY_MONTH='m';
    public static final char GROUP_BY_DAY='d';
    public static final int STATS_RANGE=5;

    private JRBeanCollectionDataSource buildGraphsStatsVO(List<TransactionToReportVO> transactions, boolean transactionsNumber,
            boolean amounts, boolean validTransactions, boolean invalidTransactions, char groupBy, Date date) {
        ArrayList<GraphsStatsSubReportVO> graphsList = new ArrayList<>();
        if (transactionsNumber){
            if (validTransactions){
                ArrayList<StatsVO> midList = new ArrayList<>();
                midList.add(getTransactionStatsByResult(groupBy, date, transactions, true));
                graphsList.add(makeSubReport(midList, "Número de transacciones validas"));            
            }
            if (invalidTransactions){
                ArrayList<StatsVO> midList = new ArrayList<>();
                midList.add(getTransactionStatsByResult(groupBy, date, transactions, false));
                graphsList.add(makeSubReport(midList, "Número de transacciones invalidas"));            
            }
//            for (StatsVO statsVO : midList) {
//                System.out.println("------------------");
//                System.out.println(statsVO.getSerieName());
//                System.out.println("PROM: "+statsVO.getProm());
//                System.out.println("MIN: "+statsVO.getMin());
//                System.out.println("MAX: "+statsVO.getMax());
//            }
        }
        if (amounts){
            if (validTransactions){
                ArrayList<StatsVO> midList = getAmountsStatsByResult(groupBy, date, transactions, true);
//                for (StatsVO statsVO : midList) {
//                    System.out.println("---------");
//                    System.out.println(statsVO.getSerieName());
//                    System.out.println("PROM: "+statsVO.getProm());
//                    System.out.println("MIN: "+statsVO.getMin());
//                    System.out.println("MAX: "+statsVO.getMax());
//                }
                graphsList.add(makeSubReport(midList,"Montos de transacciones validas"));
            }
            if (invalidTransactions){
                ArrayList<StatsVO> midList = getAmountsStatsByResult(groupBy, date, transactions, false);
//                for (StatsVO statsVO : midList) {
//                    System.out.println("---------");
//                    System.out.println(statsVO.getSerieName());
//                    System.out.println("PROM: "+statsVO.getProm());
//                    System.out.println("MIN: "+statsVO.getMin());
//                    System.out.println("MAX: "+statsVO.getMax());
//                }
                graphsList.add(makeSubReport(midList, "Montos de transacciones invalidas"));
            }            
//            System.out.println("------------------");
                        
        }
        return new JRBeanCollectionDataSource(graphsList);        
    }
    
    private GraphsStatsSubReportVO makeSubReport(ArrayList<StatsVO> midList, String title){
        JRBeanCollectionDataSource cdsSub = new JRBeanCollectionDataSource(midList);
        String path2 = "src/resources/GraphsStatsSubReport.jrxml";     
        File fsub = new File(path2);
        FileInputStream isSub = null;
        JasperReport jasperReportSub = null;
        try {
            isSub = new FileInputStream(fsub);
        } catch (FileNotFoundException ex) {
            ex.printStackTrace();
        }
        try {
            jasperReportSub = JasperCompileManager.compileReport(isSub);
        } catch (JRException ex) {
            ex.printStackTrace();
        }
        return new GraphsStatsSubReportVO(title, jasperReportSub, cdsSub);
    }

    private StatsVO getTransactionStatsByResult(char groupBy, Date date, List<TransactionToReportVO> transactions, boolean valid) {
        if(groupBy==GROUP_BY_YEAR){
            int startingYear=date.getYear()+1900-STATS_RANGE;
            StatsVO validTransactionStatsVO=new StatsVO();
            validTransactionStatsVO.setSerieName("Anios: "+startingYear+"-"+(startingYear+STATS_RANGE*2));
            int[] countTransactionStatsVO=new int[STATS_RANGE*2];
            for (TransactionToReportVO transactionToReportVO : transactions) {
                int transactionYear;
                int validationResult;
                // valida que este en los años que se desean
                try {
                    transactionYear=Integer.parseInt(transactionToReportVO.getDate().substring(0, 4));
                } catch (Exception e) {
                    continue;
                }
                if(transactionYear<(date.getYear())+1900-STATS_RANGE || transactionYear>(date.getYear())+1900+STATS_RANGE)
                    continue;
                // valida que sea valida
                try {
                    validationResult=Integer.parseInt(transactionToReportVO.getResultValidation());
                    if(valid){
                        if(validationResult!=1)
                            continue;
                    }
                    else{
                        if(validationResult!=2)
                            continue;
                    }
                } catch (Exception e) {
                    continue;
                }
                int actualYear=transactionYear-startingYear;
                countTransactionStatsVO[actualYear]++;
            }
            int total=0;
            validTransactionStatsVO.setMin(countTransactionStatsVO[0]);
            validTransactionStatsVO.setMax(countTransactionStatsVO[0]);
            for (int i = 0; i < STATS_RANGE*2; i++) {
                total+=countTransactionStatsVO[i];
                if(countTransactionStatsVO[i]<validTransactionStatsVO.getMin())
                    validTransactionStatsVO.setMin(countTransactionStatsVO[i]);
                if(countTransactionStatsVO[i]>validTransactionStatsVO.getMax())
                    validTransactionStatsVO.setMax(countTransactionStatsVO[i]);
            }
            validTransactionStatsVO.setProm(total/(double)(STATS_RANGE*2));
            validTransactionStatsVO.setStdDev(standardDerivation(validTransactionStatsVO.getProm(), STATS_RANGE*2, countTransactionStatsVO));
            return validTransactionStatsVO;
        }
        if(groupBy==GROUP_BY_MONTH){
            int startingYear=date.getYear()+1900;
            int startingMonth=date.getMonth()+1-STATS_RANGE;
            StatsVO validTransactionStatsVO=new StatsVO();
            validTransactionStatsVO.setSerieName("Meses: "+startingMonth+"-"+(startingMonth+STATS_RANGE*2));
            int[] countTransactionStatsVO=new int[STATS_RANGE*2];
            for (TransactionToReportVO transactionToReportVO : transactions) {
                int transactionMonth;
                int validationResult;
                // valida que este en los años que se desean
                try {
                    transactionMonth=Integer.parseInt(transactionToReportVO.getDate().substring(5, 7));
                } catch (Exception e) {
                    continue;
                }
                if(date.getYear()+1900!=startingYear && (transactionMonth<(date.getMonth())+1-STATS_RANGE || transactionMonth>(date.getMonth())+1+STATS_RANGE))
                    continue;
                // valida que sea valida
                try {
                    validationResult=Integer.parseInt(transactionToReportVO.getResultValidation());
                    if(validationResult!=1)
                        continue;
                } catch (Exception e) {
                    continue;
                }
                int actualMonth=transactionMonth-startingMonth;
                countTransactionStatsVO[actualMonth]++;
            }
            int total=0;
            validTransactionStatsVO.setMin(countTransactionStatsVO[0]);
            validTransactionStatsVO.setMax(countTransactionStatsVO[0]);
            for (int i = 0; i < STATS_RANGE*2; i++) {
                total+=countTransactionStatsVO[i];
                if(countTransactionStatsVO[i]<validTransactionStatsVO.getMin())
                    validTransactionStatsVO.setMin(countTransactionStatsVO[i]);
                if(countTransactionStatsVO[i]>validTransactionStatsVO.getMax())
                    validTransactionStatsVO.setMax(countTransactionStatsVO[i]);
            }
            validTransactionStatsVO.setProm(total/(double)(STATS_RANGE*2));
            validTransactionStatsVO.setStdDev(standardDerivation(validTransactionStatsVO.getProm(), STATS_RANGE*2, countTransactionStatsVO));
            return validTransactionStatsVO;
        }
        if(groupBy==GROUP_BY_DAY){
            int startingYear=date.getYear()+1900;
            int startingMonth=date.getMonth()+1;
            int startingDay=date.getDate()-STATS_RANGE;
            StatsVO validTransactionStatsVO=new StatsVO();
            validTransactionStatsVO.setSerieName("Dias: "+startingDay+"-"+(startingDay+STATS_RANGE*2));
            int[] countTransactionStatsVO=new int[STATS_RANGE*2];
            for (TransactionToReportVO transactionToReportVO : transactions) {
                int transactionDay;
                int validationResult;
                // valida que este en los años que se desean
                try {
                    transactionDay=Integer.parseInt(transactionToReportVO.getDate().substring(8));
                } catch (Exception e) {
                    continue;
                }
                if(date.getYear()+1900!=startingYear && date.getMonth()+1!=startingMonth && (transactionDay<(date.getDate())-STATS_RANGE || transactionDay>(date.getDate())+STATS_RANGE))
                    continue;
                // valida que sea valida
                try {
                    validationResult=Integer.parseInt(transactionToReportVO.getResultValidation());
                    if(validationResult!=1)
                        continue;
                } catch (Exception e) {
                    continue;
                }
                int actualDay=transactionDay-startingDay;
                countTransactionStatsVO[actualDay]++;
            }
            int total=0;
            validTransactionStatsVO.setMin(countTransactionStatsVO[0]);
            validTransactionStatsVO.setMax(countTransactionStatsVO[0]);
            for (int i = 0; i < STATS_RANGE*2; i++) {
                total+=countTransactionStatsVO[i];
                if(countTransactionStatsVO[i]<validTransactionStatsVO.getMin())
                    validTransactionStatsVO.setMin(countTransactionStatsVO[i]);
                if(countTransactionStatsVO[i]>validTransactionStatsVO.getMax())
                    validTransactionStatsVO.setMax(countTransactionStatsVO[i]);
            }
            validTransactionStatsVO.setProm(total/(double)(STATS_RANGE*2));
            validTransactionStatsVO.setStdDev(standardDerivation(validTransactionStatsVO.getProm(), STATS_RANGE*2, countTransactionStatsVO));
            return validTransactionStatsVO;
        }
        return null;
    }
    
    private ArrayList<StatsVO> getAmountsStatsByResult(char groupBy, Date date, List<TransactionToReportVO> transactions, boolean valid) {
        if(groupBy==GROUP_BY_YEAR){
            int startingYear=date.getYear()+1900-STATS_RANGE;
            ArrayList<StatsVO> statsVOs=new ArrayList<>();
            ArrayList<ArrayList<Double>> amountsPerYear=new ArrayList<>();
            int[] countTransactionStatsVO=new int[STATS_RANGE*2];
            int[] totalAmout=new int[STATS_RANGE*2];
            for (int i = 0; i < 10; i++) {
                StatsVO statsVO=new StatsVO();
                statsVO.setSerieName("Anio "+(startingYear+i));
                statsVO.setMax(-1);
                statsVO.setMin(Double.MAX_VALUE);
                statsVOs.add(statsVO);
                ArrayList<Double> yearAmounts=new ArrayList<>();
                amountsPerYear.add(yearAmounts);
            }
            for (TransactionToReportVO transactionToReportVO : transactions) {
                int transactionYear;
                int validationResult;
                // valida que este en los años que se desean
                try {
                    transactionYear=Integer.parseInt(transactionToReportVO.getDate().substring(0, 4));
                } catch (Exception e) {
                    continue;
                }
                if(transactionYear<(date.getYear())+1900-STATS_RANGE || transactionYear>(date.getYear())+1900+STATS_RANGE)
                    continue;
                // valida que sea valida
                try {
                    validationResult=Integer.parseInt(transactionToReportVO.getResultValidation());
                    if(valid){
                        if(validationResult!=1)
                            continue;
                    }
                    else{
                        if(validationResult!=2)
                            continue;
                    }
                } catch (Exception e) {
                    continue;
                }
                int actualYear=transactionYear-startingYear;
                double amount;
                try {
                    amount=Double.parseDouble(transactionToReportVO.getAmount());
                } catch (Exception e) {
                    continue;
                }
                amountsPerYear.get(actualYear).add(amount);
                if(amount<statsVOs.get(actualYear).getMin())
                    statsVOs.get(actualYear).setMin(amount);
                if(amount>statsVOs.get(actualYear).getMax())
                    statsVOs.get(actualYear).setMax(amount);
                countTransactionStatsVO[actualYear]++;
                totalAmout[actualYear]+=amount;
            }
            for (int i = 0; i < STATS_RANGE*2; i++) {
                if (countTransactionStatsVO[i] != 0)
                    statsVOs.get(i).setProm(totalAmout[i]/countTransactionStatsVO[i]);
                else{
                    statsVOs.get(i).setProm(0);
                    statsVOs.get(i).setMin(0);
                    statsVOs.get(i).setMax(0);
                }
                statsVOs.get(i).setStdDev(standardDerivation(statsVOs.get(i).getProm(), STATS_RANGE*2, amountsPerYear.get(i).toArray()));
            }
            return statsVOs;
        } else if(groupBy==GROUP_BY_MONTH){
            int startingYear=date.getYear()+1900-STATS_RANGE;
            int startingMonth=date.getMonth()+1-STATS_RANGE;
            ArrayList<StatsVO> statsVOs=new ArrayList<>();
            ArrayList<ArrayList<Double>> amountsPerYear=new ArrayList<>();
            int[] countTransactionStatsVO=new int[STATS_RANGE*2];
            int[] totalAmout=new int[STATS_RANGE*2];
            for (int i = 0; i < 10; i++) {
                StatsVO statsVO=new StatsVO();
                statsVO.setSerieName("Mes "+(startingMonth+i));
                statsVO.setMax(-1);
                statsVO.setMin(Double.MAX_VALUE);
                statsVOs.add(statsVO);
                ArrayList<Double> yearAmounts=new ArrayList<>();
                amountsPerYear.add(yearAmounts);
            }
            for (TransactionToReportVO transactionToReportVO : transactions) {
                int transactionMonth;
                int validationResult;
                // valida que este en los años que se desean
                try {
                    transactionMonth=Integer.parseInt(transactionToReportVO.getDate().substring(5, 7));
                } catch (Exception e) {
                    continue;
                }
                if(date.getYear()+1900!=startingYear && (transactionMonth<(date.getMonth())+1-STATS_RANGE || transactionMonth>(date.getMonth())+1+STATS_RANGE))
                    continue;
                // valida que sea valida
                try {
                    validationResult=Integer.parseInt(transactionToReportVO.getResultValidation());
                    if(valid){
                        if(validationResult!=1)
                            continue;
                    }
                    else{
                        if(validationResult!=2)
                            continue;
                    }
                } catch (Exception e) {
                    continue;
                }
                int actualMonth=transactionMonth-startingMonth;
                double amount;
                try {
                    amount=Double.parseDouble(transactionToReportVO.getAmount());
                } catch (Exception e) {
                    continue;
                }
                if(amount<statsVOs.get(actualMonth).getMin())
                    statsVOs.get(actualMonth).setMin(amount);
                if(amount>statsVOs.get(actualMonth).getMax())
                    statsVOs.get(actualMonth).setMax(amount);
                countTransactionStatsVO[actualMonth]++;
                totalAmout[actualMonth]+=amount;
            }
            for (int i = 0; i < STATS_RANGE*2; i++) {
                if (countTransactionStatsVO[i] != 0)
                    statsVOs.get(i).setProm(totalAmout[i]/countTransactionStatsVO[i]);
                else{
                    statsVOs.get(i).setProm(0);
                    statsVOs.get(i).setMin(0);
                    statsVOs.get(i).setMax(0);
                }
                statsVOs.get(i).setStdDev(standardDerivation(statsVOs.get(i).getProm(), STATS_RANGE*2, amountsPerYear.get(i).toArray()));
            }
            return statsVOs;
        } else if(groupBy==GROUP_BY_DAY){
            int startingYear=date.getYear()+1900-STATS_RANGE;
            int startingMonth=date.getMonth()+1-STATS_RANGE;
            int startingDay=date.getDate()-STATS_RANGE;
            ArrayList<StatsVO> statsVOs=new ArrayList<>();
            ArrayList<ArrayList<Double>> amountsPerYear=new ArrayList<>();
            int[] countTransactionStatsVO=new int[STATS_RANGE*2];
            int[] totalAmout=new int[STATS_RANGE*2];
            for (int i = 0; i < 10; i++) {
                StatsVO statsVO=new StatsVO();
                statsVO.setSerieName("Dia "+(startingDay+i));
                statsVO.setMax(-1);
                statsVO.setMin(Double.MAX_VALUE);
                statsVOs.add(statsVO);
                ArrayList<Double> yearAmounts=new ArrayList<>();
                amountsPerYear.add(yearAmounts);
            }
            for (TransactionToReportVO transactionToReportVO : transactions) {
                int transactionDay;
                int validationResult;
                // valida que este en los años que se desean
                try {
                    transactionDay=Integer.parseInt(transactionToReportVO.getDate().substring(8));
                } catch (Exception e) {
                    continue;
                }
                if(date.getYear()+1900!=startingYear && date.getMonth()+1!=startingMonth && (transactionDay<(date.getDate())-STATS_RANGE || transactionDay>(date.getDate())+STATS_RANGE))
                    continue;
                // valida que sea valida
                try {
                    validationResult=Integer.parseInt(transactionToReportVO.getResultValidation());
                    if(valid){
                        if(validationResult!=1)
                            continue;
                    }
                    else{
                        if(validationResult!=2)
                            continue;
                    }
                } catch (Exception e) {
                    continue;
                }
                int actualDay=transactionDay-startingDay;
                double amount;
                try {
                    amount=Double.parseDouble(transactionToReportVO.getAmount());
                } catch (Exception e) {
                    continue;
                }
                if(amount<statsVOs.get(actualDay).getMin())
                    statsVOs.get(actualDay).setMin(amount);
                if(amount>statsVOs.get(actualDay).getMax())
                    statsVOs.get(actualDay).setMax(amount);
                countTransactionStatsVO[actualDay]++;
                totalAmout[actualDay]+=amount;
            }
            for (int i = 0; i < STATS_RANGE*2; i++) {
                if (countTransactionStatsVO[i] != 0)
                    statsVOs.get(i).setProm(totalAmout[i]/countTransactionStatsVO[i]);
                else{
                    statsVOs.get(i).setProm(0);
                    statsVOs.get(i).setMin(0);
                    statsVOs.get(i).setMax(0);
                }
                statsVOs.get(i).setStdDev(standardDerivation(statsVOs.get(i).getProm(), STATS_RANGE*2, amountsPerYear.get(i).toArray()));
            }
            return statsVOs;
        }
        return null;
    }
    
    private double standardDerivation(double average, int n, int[] data){
        double sum=0;
        for (int i = 0; i < data.length; i++) 
            sum+=Math.pow((double)data[i]-average,2);
        return sum/n;
    }
    
    private double standardDerivation(double average, int n, Object[] data){
        double sum=0;
        for (int i = 0; i < data.length; i++) 
            sum+=Math.pow((Double)data[i]-average,2);
        return sum/n;
    }
    
}

